import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { UserEntity } from './entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcryptjs';
import { LoginDto, RegisterDto } from '../main/dto';
import { DelFlagEnum, RoleEnum } from 'src/common/enum';
import { generateUUID } from 'src/common/utils/uuid';
import { UserWidthRoleEntity } from './entities/user-width-role.entity';
import { RoleService } from '../role/role.service';
import { UserBindRoleDto, CreateUserDto, UpdateUserDto, UserBindEnterpriseDto } from './dto';
import { RoleEntity } from '../role/entities/role.entity';
import { PermEntity } from '../perm/entities/perm.entity';
import { PermService } from '../perm/perm.service';
import { EnterpriseService } from '../enterprise/enterprise.service';
import { UserDto } from './user.decorator';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UserEntity)
    private readonly userEntityRep: Repository<UserEntity>,

    @InjectRepository(UserWidthRoleEntity)
    private readonly userWidthRoleEntityRep: Repository<UserWidthRoleEntity>,

    private readonly jwtService: JwtService,

    private readonly roleService: RoleService,

    private readonly permService: PermService,

    private readonly enterpriseService: EnterpriseService,
  ) {}
  create(createUserDto: CreateUserDto) {
    return this.userEntityRep.save(createUserDto);
  }

  async findAll() {
    const res = await this.userEntityRep.find();
    return res;
  }

  findOne(userId: number) {
    return this.userEntityRep.findOne({ where: { userId } });
  }

  update(id: number, updateUserDto: UpdateUserDto) {
    return `This action updates a #${id} user`;
  }

  remove(id: number) {
    return `This action removes a #${id} user`;
  }

  async login(user: LoginDto) {
    const data = await this.userEntityRep.findOne({
      where: {
        userName: user.userName,
      },
      select: ['userId', 'password'],
    });
    if (!data?.userId) {
      throw new HttpException('账户不存在，请注册', HttpStatus.INTERNAL_SERVER_ERROR);
    }
    if (!(data?.userId && bcrypt.compareSync(user.password, data.password))) {
      throw new HttpException('账号或密码错误', HttpStatus.INTERNAL_SERVER_ERROR);
    }

    const userData = await this.findOne(data.userId);
    if (userData?.delFlag === DelFlagEnum.DELETE) {
      throw new HttpException('您已被禁用，如需正常使用请联系管理员', HttpStatus.INTERNAL_SERVER_ERROR);
    }
    const loginDate = new Date();
    await this.userEntityRep.update(
      {
        userId: data.userId,
      },
      {
        loginDate,
      },
    );

    const uuid = generateUUID();
    const token = this.createToken({ uuid, userId: data.userId });

    return token;
  }

  async register(user: RegisterDto) {
    const checkUserNameUnique = await this.userEntityRep.findOne({
      where: {
        userName: user.userName,
      },
      select: ['userName'],
    });
    if (checkUserNameUnique) {
      throw new HttpException(`注册用户'${user.userName}'失败，注册账号已存在`, HttpStatus.INTERNAL_SERVER_ERROR);
    }
    const salt = bcrypt.genSaltSync(10);
    if (user.password) {
      user.password = await bcrypt.hashSync(user.password, salt);
    }
    const loginDate = new Date();
    const saveUser: CreateUserDto = {
      ...user,
      nickName: user.userName,
      loginDate,
    };
    const createRes = await this.create(saveUser);
    // const userRoles: RoleEnum[] = user?.roles || [];
    const roleIds = [RoleEnum.sys_visitor];
    await this.userBindRole({ userId: createRes.userId, roleIds });
    const { password, ...safeUser } = createRes;
    return safeUser;
  }

  async userBindRole(data: UserBindRoleDto) {
    const { userId, roleIds } = data;
    const roles = await this.roleService.findAll();
    if (roleIds.includes(RoleEnum.sys_admin)) {
      throw new HttpException('不能绑定超级管理员', HttpStatus.BAD_REQUEST);
    }
    if (roleIds.some((id) => !roles.map((role) => role.roleId).includes(id))) {
      throw new HttpException('身份不存在', HttpStatus.BAD_REQUEST);
    }
    const entitys = roleIds.map((roleId) => {
      const entity = new UserWidthRoleEntity();
      return Object.assign(entity, {
        roleId,
        userId,
      });
    });
    return await this.userWidthRoleEntityRep.save(entitys);
  }

  async userBindEnterprise(data: UserBindEnterpriseDto) {
    const { userId, enterpriseId } = data;
    const enterpriseData = await this.enterpriseService.findOne(enterpriseId);
    if (!enterpriseData?.enterpriseId) {
      throw new HttpException('企业不存在', HttpStatus.INTERNAL_SERVER_ERROR);
    }
    // 校验 如果企业已经存在拥有创始人身份的用户 且 当前绑定用户也拥有创始人身份 则不能绑定
    const entity = this.userEntityRep.createQueryBuilder('user');
    entity.leftJoinAndMapMany('user.roles', UserWidthRoleEntity, 'userRole', 'user.userId = userRole.userId');
    entity.andWhere(`user.enterpriseId = :enterpriseId`, { enterpriseId: enterpriseId });
    entity.andWhere(`userRole.roleId = :roleId`, { roleId: RoleEnum.enterprise_boss });
    const boss = await entity.getOne();
    if (boss) {
      const userInfo = await this.getUserInfo(userId);
      const userRoles = userInfo.roles.map((role) => role.roleId);
      if (userRoles.includes(RoleEnum.enterprise_boss)) {
        throw new HttpException('关联企业已拥有创始人', HttpStatus.INTERNAL_SERVER_ERROR);
      }
    }
    const res = await this.userEntityRep.update(
      {
        userId,
      },
      {
        enterpriseId,
      },
    );
    if (res.affected != 1) {
      throw new HttpException('绑定失败', HttpStatus.INTERNAL_SERVER_ERROR);
    }
    return true;
  }

  async getUserInfo(userId: number): Promise<UserDto> {
    const user = await this.findOne(userId);
    if (!user) throw new HttpException('用户不存在', HttpStatus.INTERNAL_SERVER_ERROR);
    const userWithRoleEntities = await this.userWidthRoleEntityRep.find({
      where: { userId: user.userId },
    });
    const roleIds = userWithRoleEntities.map((item) => item.roleId);
    const roles = await this.roleService.findRoles({
      where: {
        roleId: In(roleIds),
      },
    });
    const permIds = (
      await this.roleService.findRoleWidthPerm({
        where: {
          roleId: In(roleIds),
        },
      })
    ).map((item) => item.permId);
    const perms = await this.permService.findPerms({
      where: {
        permId: In(permIds),
      },
    });
    return {
      ...user,
      roles,
      perms,
    };
  }

  createToken(payload: { uuid: string; userId: number }) {
    const accessToken = this.jwtService.sign(payload);
    return accessToken;
  }
}
